package geppetto.cat.models;

 

import geppetto.cat.alignments.Alignment;
import geppetto.cat.alignments.AlignmentEvaluator;
import geppetto.cat.alignments.AlignmentsSet;
import geppetto.cat.alignments.AlignmentEvaluator.Evaluation;
import geppetto.cat.alignments.output.AlignerOutputLatex;
import geppetto.cat.common.StaticTools;
import geppetto.cat.constrains.ConstrainedProjectionStats;
import geppetto.cat.corpus.BilingualCorpus;
import geppetto.cat.models.stats.EStepStats;
import geppetto.cat.programs.BaseExp;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;




/**
 * Abstract class that defines common methods for all word alignments algorithms
 * 
 * @author javg
 * 
 */
public abstract class AbstractModel {


	public BilingualCorpus _corpus;
	
	public int _nSentences;

	public int _sourceVocabSize, _foreignVocabSize;

	public int _numericUnderFlow = 0;
	public int _numberTrainingIterations;
	public int _currentTrainingIteration;

	// Indicates if the model is already train or not. Decoding is only possible
	// on a trained model
	public boolean _trained = false;


	public AbstractModel() {

	}

	public BilingualCorpus getCorpus() {
		return _corpus;
	}

	public void setCorpus(BilingualCorpus corpus) {
		_corpus = corpus;
	}

	public AbstractModel(BilingualCorpus corpus) {
		System.out.println("Started the Initialization of " + getName());
		_corpus = corpus;
		_sourceVocabSize = corpus.getSourceSize();
		_foreignVocabSize = corpus.getForeignSize();
		_nSentences = corpus.getNumberOfTrainingSentences();
		System.out.println("Ended the Initialization of " + getName());
	}

	
	/** 
	 * Saves the model to a given directory
	 * Must save
	 * - Description of the corpus used
	 * - Information regarding the model
	 * 	 - Iterations trained
	 *   - Any model parameters 
	 *   - Different model parameters
	 * - Time stamp
	 * @param directory
	 */
	public void saveModel(String directory){
		if(!trained()){
			System.out.println(getName()+": Model not trained. Cannot save model");
			System.exit(-1);
		}
		//Save corpus
		_corpus.saveDescription(directory);
		try {
			PrintStream file = new PrintStream(new FileOutputStream(directory+ "stamp"));
			printStamp(file);
		} catch (FileNotFoundException e) {
			System.out.println(getName()+": Could not save model");
			System.exit(-1);
		}
	}

	public void printStamp(PrintStream file){
		DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        java.util.Date date = new Date();
        System.out.println("Current Date Time : " + dateFormat.format(date));
	}
	
	
	
	
	// Return the likelihood of the corpus
	public abstract EStepStats eStep();

	public boolean trained() {
		return _trained;
	}
	public void setTrained(){
		_trained = true;
	}

	public abstract void mStep();

	public abstract String getName();

	public EStepStats createModelStats(){
		return new EStepStats();
	}
	
	// Normal EM training
	public void train(int iterations, boolean saveModels, String saveModelsDir) {
		_numberTrainingIterations = iterations;
		long initTrain = System.currentTimeMillis();
		System.out.println("Starting " + getName() + " Training");
		initializeTrain();
		EStepStats d = createModelStats(), old = createModelStats();
		setTrained();
		for (int k = 0; k < iterations; k++) {
			_currentTrainingIteration = k+1;
			// /DEBUG CODE
			System.out.println("Iteration " + (k + 1));
			System.out.flush();
			// E-Step
			System.out.println(" e Step");
			d.startTime();
			d.add(eStep());
			d.stopTime();
			System.out.println(d.makeVerbose(old));
			// M-Step
			old = d;
			d = createModelStats();
			mStep();
			System.out.println("m Step");
			System.out.flush();
			
			if(saveModels){
				String dir = saveModelsDir +"/iter"+k+"/";
				StaticTools.createDir(dir);
				saveModel(dir);
			}
		}
		
		finalizeTrain();
		long finitTrain = System.currentTimeMillis();
		System.out.println("Ended " + getName() + " Training: Took " + formatTime(finitTrain - initTrain));
	}

	java.text.DecimalFormat fmt = new java.text.DecimalFormat();
	
	private String formatTime(long duration) {
		StringBuilder sb = new StringBuilder();
		double d = duration / 1000;
		fmt.applyPattern("00");
		sb.append(fmt.format((int) (d / (60 * 60))) + ":");
		d -= ((int) d / (60 * 60)) * 60 * 60;
		sb.append(fmt.format((int) (d / 60)) + ":");
		d -= ((int) d / 60) * 60;
		fmt.applyPattern("00.0");
		sb.append(fmt.format(d));
		return sb.toString();
	}
	
	
	/**
	 * 
	 * @param iterations
	 * @param sentenceSource
	 * @return A bunch of evaluations
	 * ArrayList with 6 values
	 * Viterbi decoding: 
	 * 1 - All 
	 * 2 - Rare
	 * 3 - Common
	 * Posteriors decoding
	 * 4 - All
	 * 5 - Rare
	 * 6 - Common
	 */
	public ArrayList<Evaluation[]> trainWithResults(int iterations, byte sentenceSource, boolean saveModels, String saveModelsDir) {
		ArrayList<Evaluation[]> evals = new ArrayList<Evaluation[]>();
		Evaluation[] viterbiAll = new Evaluation[iterations];
		Evaluation[] viterbiRare = new Evaluation[iterations];
		Evaluation[] viterbiCommon = new Evaluation[iterations];
		Evaluation[] posterirAll = new Evaluation[iterations];
		Evaluation[] posteriorRare = new Evaluation[iterations];
		Evaluation[] posteriorCommon = new Evaluation[iterations];
		System.out.println("Starting " + getName() + " Training");
		long initTrain = System.currentTimeMillis();
		initializeTrain();
		EStepStats d = createModelStats(), old = createModelStats();
		setTrained();
		for (int k = 0; k < iterations; k++) {
			_currentTrainingIteration = k+1;
			System.out.println("Iteration " + (k + 1));
			System.out.flush();
			// E-Step
			System.out.println(" e Step");
			d.startTime();
			d.add(eStep());
			d.stopTime();
			System.out.println(d.makeVerbose(old));
			// M-Step
			old = d;
			d = new EStepStats();
			
			mStep();
			System.out.println("m Step");
			System.out.flush();
			//
			AlignmentsSet predicted = viterbiAlignments(sentenceSource);
			viterbiAll[k] = AlignmentEvaluator.evaluate(predicted, _corpus
					.getAlignments(sentenceSource));
			Evaluation[] viterbiboth = AlignmentEvaluator.evaluateRareWords(predicted, _corpus
					.getAlignments(sentenceSource),_corpus);
			viterbiRare[k]=viterbiboth[0];
			viterbiCommon[k]=viterbiboth[1];
			float trehs = tuneTreshholdAER(sentenceSource, false);
			AlignmentsSet predicted2 = posteriorAlignments(sentenceSource,trehs,true,true);
			posterirAll[k] = AlignmentEvaluator.evaluate(predicted2, _corpus
					.getAlignments(sentenceSource));
			Evaluation[] posteriorboth = AlignmentEvaluator.evaluateRareWords(predicted2, _corpus
					.getAlignments(sentenceSource),_corpus);
			posteriorRare[k]=posteriorboth[0];
			posteriorCommon[k]=posteriorboth[1];
			printPosteriorCurves(_corpus, sentenceSource, true, "pc-iter" + k , 40);
			System.out.println("area-" + k + " " + getPosteriorCurvesArea(_corpus, sentenceSource, true));
			
		 	if(saveModels){
				String dir = saveModelsDir +"/iter"+k+"/";
				StaticTools.createDir(dir);
				saveModel(dir);
			}
		}
		
		finalizeTrain();
		long finitTrain = System.currentTimeMillis();
		System.out.println("Ended " + getName() + " Training: Took " + formatTime(finitTrain - initTrain));
		evals.add(viterbiAll);
		evals.add(viterbiRare);
		evals.add(viterbiCommon);
		evals.add(posterirAll);
		evals.add(posteriorRare);
		evals.add(posteriorCommon);
		return evals;
	}
	
	
	public abstract void initializeTrain();

	public abstract void finalizeTrain();

	

	
	
	
	/**
	 * Gets the posterior probability of a given phrase
	 * @param sentenceNumber
	 * @param sentenceSource
	 * @param foreingSentence
	 * @param sourceSentence
	 * @param startSourceIndex
	 * @param endSourceIndex
	 * @param startForeignIndex
	 * @param endForeignIndex
	 * @return
	 */
	public abstract double getPhrasePosterior(int sentenceNumber,
	byte sentenceSource, int[] foreingSentence, int[] sourceSentence,
	int startSourceIndex, int endSourceIndex, int startForeignIndex,
	int endForeignIndex);

	/**
	 * Gets the posterior probability of a given null phrase
	 * @param sentenceNumber
	 * @param sentenceSource
	 * @param foreingSentence
	 * @param sourceSentence
	 * @param startForeignIndex
	 * @param endForeignIndex
	 * @return
	 */
	public abstract double getNullPhrasePosterior(int sentenceNumber,
			byte sentenceSource, int[] foreingSentence, int[] sourceSentence,
			int startForeignIndex, int endForeignIndex);
	
	/////////////////////////////////////////////////////////////////
	// DECODING CODE
   /////////////////////////////////////////////////////////////////

	
	
	public abstract Alignment viterbiAlignment(int sentenceNumber,
			byte sentenceSource, boolean projectPosteriors, ConstrainedProjectionStats stats);
	
	
	public AlignmentsSet viterbiAlignments(int[] sample, byte sentenceSource){
		
		return viterbiAlignments(sample, sentenceSource, false);
	}
	
	/** return the alignments of the selected in sample from the training corpus */
	public AlignmentsSet viterbiAlignments(int[] sample, byte sentenceSource, boolean projectPosteriors) {
		assert _trained;
		ConstrainedProjectionStats stats = new ConstrainedProjectionStats();
		AlignmentsSet set = new AlignmentsSet();
		for (int i = 0; i < sample.length; i++) {
			set.addAlignment(viterbiAlignment(sample[i], sentenceSource, projectPosteriors,stats));
		}
		if(projectPosteriors){
			System.out.println("Decoding Projection Stats \n\n" + stats.prettyPrint());
		}

		return set;
	}
	
	public AlignmentsSet viterbiAlignments(byte sentenceSource){
		return viterbiAlignments(sentenceSource, false);
	}
	
	public AlignmentsSet viterbiAlignments(byte sentenceSource, boolean projectPosteriors) {
		assert _trained;
		AlignmentsSet set = new AlignmentsSet();
		ConstrainedProjectionStats stats = new ConstrainedProjectionStats();
		for (int i = 0; i < _corpus.getNumSentences(sentenceSource); i++) {
			set.addAlignment(viterbiAlignment(i, sentenceSource, projectPosteriors, stats));
		}
		if(projectPosteriors && stats != null){
			System.out.println("Decoding Projection Stats \n\n" + stats.prettyPrint());
		}
		
		return set;
	}

	/** return the alignments of the selected in sample from the training corpus */
	public AlignmentsSet posteriorAlignments(int[] sample, byte sentenceSource,
			float treshold, boolean projectPosteriors, boolean printStats ) {
		assert _trained;
		ConstrainedProjectionStats stats = new ConstrainedProjectionStats();
		AlignmentsSet set = new AlignmentsSet();
		for (int i = 0; i < sample.length; i++) {
			set.addAlignment(posteriorDecodingAlignment(sample[i],
					sentenceSource, treshold,projectPosteriors,stats));
		}
		if(projectPosteriors && printStats){
			System.out.println("Decoding Projection Stats \n\n" + stats.prettyPrint());
		}
		return set;
	}

	

	public AlignmentsSet posteriorAlignments(byte sentenceSource, float treshold, boolean projectPosteriors, boolean printStats) {
		assert _trained;
		AlignmentsSet set = new AlignmentsSet();
		ConstrainedProjectionStats stats = new ConstrainedProjectionStats();
		for (int i = 0; i < _corpus.getNumSentences(sentenceSource); i++) {
			set.addAlignment(posteriorDecodingAlignment(i, sentenceSource,
					treshold,projectPosteriors,stats));
		}
		if(projectPosteriors && printStats){
			System.out.println("Decoding Projection Stats \n\n" + stats.prettyPrint());
		}
		return set;
	}

	

	

	public abstract Alignment posteriorDecodingAlignment(int sentenceNumber,
			byte sentenceSource, float treshhold, boolean projectPosteriors, ConstrainedProjectionStats stats);

	/////////////////////////////////////////////////////////////////
	// POSTERIOR TUNNING CODE
   /////////////////////////////////////////////////////////////////
	

	
	public double getPosteriorCurvesArea(BilingualCorpus corpus, byte sentenceSource, boolean projectPosteriors){
		double area1 = getPosteriorCurvesArea(getPosteiorCurvesAux(corpus, sentenceSource, projectPosteriors, 100, 0,0.9)); 
		double area2 = getPosteriorCurvesArea(getPosteiorCurvesAux(corpus, sentenceSource, projectPosteriors, 1000, 0.9, 0.1));
		return area1+area2;
	}
	/**
	 * Gets the area under the graph. Since we are comparing this agains other graphs we must make sure 
	 * to sum only over one part of the graph.
	 */
	public double getPosteriorCurvesArea(double[][] points){
		
		double sum = 0;
		int len = points[0].length;
		for(int i =len-2; i >= 0; i--){
			double increaseInPrecision = points[1][i+1] - points[1][i];
			double recallAverage = (points[2][i+1] + points[2][i])/2;
			sum+=increaseInPrecision*recallAverage;
		}
		return sum;
	}
	
	public double[][] getPosteiorCurvesAux(BilingualCorpus corpus, byte sentenceSource, boolean projectPosteriors, int divisions
			,double lowerThres, double higherTresh){
		int[] sentences = new int[corpus.getNumSentences(sentenceSource)];
		for (int i = 0; i < sentences.length; i++) {
			sentences[i] = i;
		}
		AlignmentsSet predicted = posteriorAlignments(sentences,
				sentenceSource, 1f , projectPosteriors,false);
		for(Alignment a : predicted._alignments){
			a.resetPoints();
		}
		double increment = (higherTresh-lowerThres)/divisions;
		double[][] output = new double[3][divisions];
		int i =0;
		for (double thresh = lowerThres; thresh <= higherTresh; thresh+=increment) {
			predictAux(predicted, thresh );
			float[] results = AlignmentEvaluator.calculateMeasures(predicted,
					corpus.getAlignments(sentences, sentenceSource));
			output[0][i]=thresh;
			output[1][i]=results[0];
			output[2][i]=results[1];
			for(Alignment a : predicted._alignments){
				a.resetPoints();
			}
			i++;
		}
		return output;
	}
	
	public void printPosteriorCurves(BilingualCorpus corpus, byte sentenceSource, boolean projectPosteriors,  String grepString, int divisions){
		double[][] points = getPosteiorCurvesAux(corpus, sentenceSource, projectPosteriors, divisions, 0.1, 0.9);
		int len = points[0].length;
		for(int i = 0; i < len; i++){
			System.out.println(grepString + " " + points[0][i] + " P: " + points[1][i] + " R: " + points[2][i]);
		}
	//	System.out.println("area-"+grepString + " "+ getPosteriorCurvesArea(points));
		//		int[] sentences = new int[corpus.getNumSentences(sentenceSource)];
//		for (int i = 0; i < sentences.length; i++) {
//			sentences[i] = i;
//		}
//		AlignmentsSet predicted = posteriorAlignments(sentences,
//				sentenceSource, 1f , projectPosteriors,false);
//		for(Alignment a : predicted._alignments){
//			a.resetPoints();
//		}
//		
//		for (double i = 0; i <= divisions; i++) {
//			predictAux(predicted, 0.9 * i / divisions);
//			float[] results = AlignmentEvaluator.calculateMeasures(predicted,
//					corpus.getAlignments(sentences, sentenceSource));
//			
//			for(Alignment a : predicted._alignments){
//				a.resetPoints();
//			}
//			System.out.println(grepString + " " + 1f * i / divisions + " P: " + results[0] + " R: " + results[1]);
//		}
	}

	

//	public float tuneTreshholdPrecisionRecall(byte sentenceSource,
//			boolean precision, boolean projectPosteriors) {
//		System.out.print("\t\tTunning T: ");
//		System.out.flush();
//		float treshhold = 0f;
//		int divisions = 100;
//		float current = 0;
//		for (int i = 1; i <= divisions; i++) {
//			// System.out.println(i);
//			AlignmentsSet predicted = posteriorAlignments(sentenceSource, 1f
//					* i / divisions, projectPosteriors,false);
//			float[] results = AlignmentEvaluator.calculateMeasures(predicted,
//					_corpus.getAlignments(sentenceSource));
//			// System.out.println(" " + results[2] + " "+ (1f*i/divisions) );
//			if (precision) {
//				if (results[0] > current) {
//					current = results[0];
//					treshhold = 1f * i / divisions;
//				}
//			} else {
//				if (results[1] > current) {
//					current = results[1];
//					treshhold = 1f * i / divisions;
//				}
//			}
//			// System.out.println(treshhold+", "+"Precision" + results[0]+
//			// "Recall " + results[1] );
//		}
//		System.out.println(treshhold + ", " + current);
//		return treshhold;
//	}

	/**
	 * tune threshold to get expected number of alignments on given sample of
	 * training data. implemented as binary search.
	 */
	public float tuneThresholdNumPoints(int numPts, int[] sample,
			byte sentenceSource, boolean projectPosteriors) {
		System.out.print("\t\tTunning T for " + numPts + " points : ");
		System.out.flush();
		float min = 0f;
		int numMin = posteriorAlignments(sample, sentenceSource, min, projectPosteriors,false)
				.numberOfAlignedPoints();
		float max = 1f;
		int numMax = posteriorAlignments(sample, sentenceSource, max, projectPosteriors,false)
				.numberOfAlignedPoints();
		// 10 binary steps means difference of < 10^-3
		for (int i = 1; i <= 10; i++) {
			float mid = (min + max) / 2;
			int numMid = posteriorAlignments(sample, sentenceSource, mid, projectPosteriors,false)
					.numberOfAlignedPoints();
			if (numMid < numPts) {
				max = mid;
				numMax = numMid;
			} else {
				min = mid;
				numMin = numMid;
			}
		}
		System.out.println(min + max / 2 + " " + numMin + " " + numMax);
		return min + max / 2;
	}

	
	
/// NEW TUNNING CODE
	
	
	
	
	public float tuneTreshholdAER(byte sentenceSource, boolean projectPosteriors){
		int[] sentences = new int[_corpus.getNumSentences(sentenceSource)];
		for (int i = 0; i < sentences.length; i++) {
			sentences[i] = i;
		}
		return tuneTresholdAERAux(sentences, sentenceSource, projectPosteriors);
	}
	
	public float tuneTreshholdF1(byte sentenceSource, float alpha, boolean projectPosteriors) {
		int[] sentences = new int[_corpus.getNumSentences(sentenceSource)];
		for (int i = 0; i < sentences.length; i++) {
			sentences[i] = i;
		}
		return tuneTreshholdBalancedF1Aux(sentences, sentenceSource, alpha, projectPosteriors);
	}
	
	public void predictAux(AlignmentsSet al, double treshold){
		for(Alignment a : al.getAlignments()){
			for(int i = 0; i < a.getSourceLenght(); i++){
				for(int j = 0; j < a.getForeignLenght(); j++){
					if(a._posteriors[i][j] > treshold){
						a.add(i, j);
					}
				}
			}
		}
//		System.out.println("Predicted for trehshold" + treshold);
//		for(Alignment a: al.getAlignments() ){
//			AlignerOutput.output(a, System.out);
//		}
	}
	
	
//	 Fix a specic precision or recall
	public float tuneTreshholdPrecisionRecallFixOtherValue(byte sentenceSource,
			boolean precision, double fixValue, double fixError, boolean projectPosteriors) {
		
		System.out.print("\t\tTunning T: ");
		System.out.flush();
		int[] sentences = new int[_corpus.getNumSentences(sentenceSource)];
		for (int i = 0; i < sentences.length; i++) {
			sentences[i] = i;
		}
		AlignmentsSet predicted = posteriorAlignments(sentences,
				sentenceSource, 1f , projectPosteriors,false);
				
		float treshhold = 0f;
		int divisions = 100;
		float current = 0, other = 0;
		for (int i = 1; i <= divisions; i++) {
			for(Alignment a : predicted._alignments){
				a.resetPoints();
			}
			// System.out.println(i);
			predictAux(predicted, 1f * i / divisions);
			float[] results = AlignmentEvaluator.calculateMeasures(predicted,
					_corpus.getAlignments(sentences, sentenceSource));
			if (precision) {
				if ((results[0] > current)
						&& (results[1] - fixValue)>0) {
					current = results[0];
					other = results[1];
					treshhold = 1f * i / divisions;
				}
			} else {
				if ((results[1] > current)
						&& (results[0] - fixValue)>0) {
					current = results[1];
					other = results[0];
					treshhold = 1f * i / divisions;
				}
			}
//			 System.out.println(treshhold+", "+"Precision" + results[0]+
//			 "Recall " + results[1] + "Fix" + fixValue + " current " + current + "value " + 
//			 Math.abs(results[0] - fixValue) + fixError + " tried " + 1f * i / divisions);
		}
		if (precision){
			System.out.println(treshhold + ", Precision " + current
					+ "  Recall " + other);
		}else{
			System.out.println(treshhold + ", Recall " + current
					+ " Precision " + other);
		}
		return treshhold;
	}

	
	/**
	 * Could be doing kind of line search instead of trying all points since function is convex
	 * @param sentences
	 * @param sentenceSource
	 * @param projectPosteriors
	 * @return
	 */
	public float tuneTresholdAERAux(int[] sentences, byte sentenceSource, boolean projectPosteriors){
		System.out.print("Tunning T: ");
		AlignmentsSet predicted = posteriorAlignments(sentences,
				sentenceSource, 1f , projectPosteriors,false);
		for(Alignment a : predicted._alignments){
			a.resetPoints();
		}
		float treshhold = 0f;
		int divisions = 5;
		double aer = 1;
		
		
		for (int i = 1; i <= divisions; i++) {
			predictAux(predicted, 1f * i / divisions);
			float[] results = AlignmentEvaluator.calculateMeasures(predicted,
					_corpus.getAlignments(sentences, sentenceSource));
			// System.out.println(" " + results[2] + " "+ (1f*i/divisions) );
			if (results[2] < aer) {
				aer = results[2];
				treshhold = 1f * i / divisions;
			}
			for(Alignment a : predicted._alignments){
				a.resetPoints();
			}
			//System.out.println(1f * i / divisions + " " + results[2]);
		}
		System.out.println(treshhold + ", " + aer);
		return treshhold;
	}
	
	public float tuneTreshholdBalancedF1Aux(int[] sentences, byte sentenceSource, float alpha,boolean projectPosteriors){
		System.out.print("Tunning T: ");
		AlignmentsSet predicted = posteriorAlignments(sentences,
				sentenceSource, 1f , projectPosteriors,false);
		for(Alignment a : predicted._alignments){
			a.resetPoints();
		}
		float treshhold = 0f;
		int divisions = 100;
		float current = 0;
		float precisionS = 0;
		float recallS = 0;
		
		for (int i = 1; i <= divisions; i++) {
			predictAux(predicted, 1f * i / divisions);
			float[] results = AlignmentEvaluator.calculateMeasures(predicted,
					_corpus.getAlignments(sentences, sentenceSource));
			// System.out.println(" " + results[2] + " "+ (1f*i/divisions) );
			float precision = results[0];
			float recall = results[1];
			float f1 = 1 / (alpha / precision + (1 - alpha) / recall);
			if (f1 > current) {
				current = f1;
				precisionS = precision;
				recallS = recall;
				treshhold = 1f * i / divisions;
			}
			for(Alignment a : predicted._alignments){
				a.resetPoints();
			}
			//System.out.println(1f * i / divisions + " " + results[2]);
		}
		System.out.println(treshhold + ", " + current + "Precision "
				+ precisionS + " Recall " + recallS);
		return treshhold;
	}
	
	
	//OUPUT CODE

	public void outputAlignment(Alignment al, Alignment gold,
			BilingualCorpus corpus, PrintStream stream, int sentenceNumber)
			throws UnsupportedEncodingException {
		AlignerOutputLatex.outputWithGoldPostLatex(al, gold, corpus, stream,
				"Sentence nr " + sentenceNumber);
		Evaluation fRes = AlignmentEvaluator.evaluate(al, gold);
		stream.println("\\");
		stream.println("\\begin{tabular}{|c|ccccccccc|}");
		stream.println("&\\multicolumn{9}{|c|}{" + "Sentence nr "
				+ sentenceNumber + "}\\\\");
		stream.println("\\hline");
		stream.println("" + fRes.latexTableHeader());
		stream.println("F " + fRes.toLatexTableEntry());
		stream.println("\\hline");
		stream.println("\\end{tabular}");
	}

	public void outputAlignments(AlignmentsSet al, AlignmentsSet gold,
			BilingualCorpus corpus, PrintStream stream)
			throws UnsupportedEncodingException {
		BaseExp.printBegin(stream);
		for (int i = 0; i < al.getAlignments().size(); i++) {
			Alignment a = al.getAlignmentByPos(i);
			Alignment g = gold.getAlignmentByPos(i);
			if (a.getForeignLenght() > 15 || a.getSourceLenght() > 15)
				continue;
			outputAlignment(a, g, corpus, stream, i);
			stream.println("\\clearpage\\pagebreak");
		}
		Evaluation res = AlignmentEvaluator.evaluate(al, gold);
		stream.println("\\begin{tabular}{|c|ccccccccc|}");
		stream.println("&\\multicolumn{9}{|c|}{" + "Total " + "}\\\\");
		stream.println("\\hline");
		stream.println("" + res.latexTableHeader());
		stream.println("F " + res.toLatexTableEntry());
		stream.println("\\hline");
		stream.println("\\end{tabular}");
		stream.println("\\clearpage\\pagebreak");
		BaseExp.printEnd(stream);
		stream.close();
	}

}
